package com.xuan.friend.service.question.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.xuan.common.core.constants.CacheConstants;
import com.xuan.common.core.constants.Constants;
import com.xuan.common.core.domain.TableDataInfo;
import com.xuan.common.core.enums.QuestionStatus;
import com.xuan.common.core.enums.ResultCode;
import com.xuan.common.core.exception.BusinessException;
import com.xuan.common.core.utils.ThreadLocalUtils;
import com.xuan.common.redis.service.RedisService;
import com.xuan.friend.domain.question.Question;
import com.xuan.friend.domain.question.dto.QuestionQueryDTO;
import com.xuan.friend.domain.question.es.QuestionES;
import com.xuan.friend.domain.question.vo.HotQuestionVo;
import com.xuan.friend.domain.question.vo.QuestionDetailVo;
import com.xuan.friend.domain.question.vo.QuestionVo;
import com.xuan.friend.domain.user.UserSubmit;
import com.xuan.friend.elasticsearch.QuestionRepository;
import com.xuan.friend.manager.QuestionCacheManager;
import com.xuan.friend.mapper.question.QuestionMapper;
import com.xuan.friend.mapper.user.UserSubmitMapper;
import com.xuan.friend.service.question.QuestionService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question>
        implements QuestionService {

    @Resource
    private QuestionMapper questionMapper;

    @Resource
    private UserSubmitMapper userSubmitMapper;

    @Resource
    private QuestionRepository questionRepository;

    @Resource
    private QuestionCacheManager questionCacheManager;



    @Override
    public TableDataInfo listQuestion(QuestionQueryDTO questionQueryDTO) {
        String keyword = questionQueryDTO.getKeyword();
        Integer difficulty = questionQueryDTO.getDifficulty();
        // 1. 查看是否需要查询数据库
        long count = questionRepository.count();        // 可以直接查询 elasticsearch 中数据是否存在
        if (count <= 0) {
            // elasticsearch 没有数据,直接查询数据库并将数据放到 elasticsearch 中
            refreshQuestion();
        }
        // 2. 直接从 elasticsearch 中查询
        // 1) 组织基本查询条件
        Integer pageNum = questionQueryDTO.getPageNum();
        Integer pageSize = questionQueryDTO.getPageSize();
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        PageRequest pageRequest = PageRequest.of(pageNum - 1, pageSize, sort);
        // 2) 根据查询条件调用对应方法
        Page<QuestionES> questionESPage = null;
        if (StringUtils.isBlank(keyword) && (difficulty == null || difficulty < 1 || difficulty > 3)) {
            // 没有查询条件:关键词和难度都为空
            questionESPage = questionRepository.findAll(pageRequest);
        } else if (StringUtils.isBlank(keyword) && (difficulty != null && difficulty >= 1 && difficulty <= 3)) {
            // 难度不为空
            questionESPage = questionRepository.findByDifficulty(difficulty, pageRequest);
        } else if (StringUtils.isNotBlank(keyword) && (difficulty == null || difficulty < 1 || difficulty > 3)) {
            // 关键词不为空
            questionESPage = questionRepository.findByTitleOrContent(keyword, keyword, pageRequest);
        } else {
            // 关键词和难度都不为空
            questionESPage = questionRepository.findByTitleOrContentAndDifficulty(keyword, keyword, difficulty, pageRequest);
        }

        if (questionESPage == null || questionESPage.getTotalElements() <= 0) {
            return TableDataInfo.empty();
        }
        // 查询的数据不为空
        List<QuestionES> content = questionESPage.getContent();
        List<QuestionVo> questionVoList = BeanUtil.copyToList(content, QuestionVo.class);
        long total = questionESPage.getTotalElements();
        // 补充用户解答题目的状态
        assembleQuestionVo(questionVoList);
        return TableDataInfo.success(questionVoList, total);
    }

    private void assembleQuestionVo(List<QuestionVo> questionVoList) {
        Long userId = ThreadLocalUtils.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            // 未登录状态下设置 status 字段
            return;
        }

        // 从 redis 中查询用户已通过和未通过的题目列表(如果不存在则刷新缓存)
        List<Long> passQuestionIdList = questionCacheManager.getUserQuestionStatusList(userId, QuestionStatus.PASSED.getValue());
        List<Long> failQuestionIdList = questionCacheManager.getUserQuestionStatusList(userId, QuestionStatus.FAILED.getValue());
        for (QuestionVo questionVo : questionVoList) {
            Long questionId = questionVo.getQuestionId();
            if (CollectionUtil.isNotEmpty(passQuestionIdList) && passQuestionIdList.contains(questionId)) {
                questionVo.setStatus(QuestionStatus.PASSED.getValue());
            } else if (CollectionUtil.isNotEmpty(failQuestionIdList) && failQuestionIdList.contains(questionId)) {
                questionVo.setStatus(QuestionStatus.FAILED.getValue());
            }
        }
    }


    @Override
    public QuestionDetailVo questionDetail(Long questionId, Long contestId) {
        // 1. 先从 ES 中查询题目数据是否存在
        QuestionES questionES = questionRepository.findById(questionId).orElse(null);
        QuestionDetailVo questionDetailVo = new QuestionDetailVo();
        if (questionES != null) {
            BeanUtils.copyProperties(questionES, questionDetailVo);
        } else {
            // 2. 查询数据库,看题目是否存在
            Question question = questionMapper.selectById(questionId);
            if (question == null) {
                throw new BusinessException(ResultCode.FAILED_NOT_EXISTS, "题目不存在");
            }
            // 刷新数据到 elasticsearch
            refreshQuestion();
            BeanUtils.copyProperties(question, questionDetailVo);
        }

        // 3. 将用户的答题记录查询出来,并替换默认代码(如果存在)
        Long userId = ThreadLocalUtils.get(Constants.USER_ID, Long.class);
        UserSubmit userSubmit = userSubmitMapper.selectOne(new LambdaQueryWrapper<UserSubmit>()
                .select(UserSubmit::getUserCode)
                .eq(UserSubmit::getUserId, userId)
                .eq(UserSubmit::getQuestionId, questionId)
                .isNull(contestId == null, UserSubmit::getContestId)
                .eq(contestId != null, UserSubmit::getContestId, contestId));
        if (userSubmit != null && StringUtils.isNotBlank(userSubmit.getUserCode())) {
            questionDetailVo.setDefaultCode(userSubmit.getUserCode());
        }
        return questionDetailVo;
    }

    @Override
    public String prevQuestion(Long questionId) {
        Long questionListSize = questionCacheManager.getQuestionListSize();
        // 先从 redis 查询, 查询不到刷新缓存
        if (questionListSize == null || questionListSize < 1) {
            questionCacheManager.refreshCache();
        }
        return questionCacheManager.prevQuestion(questionId).toString();
    }

    @Override
    public String nextQuestion(Long questionId) {
        Long questionListSize = questionCacheManager.getQuestionListSize();
        // 先从 redis 查询, 查询不到刷新缓存
        if (questionListSize == null || questionListSize < 1) {
            questionCacheManager.refreshCache();
        }
        return questionCacheManager.nextQuestion(questionId).toString();
    }

    @Override
    public List<HotQuestionVo> getHotQuestionList() {
        // 获取热题id
        List<Long> hotQuestionIdList = questionCacheManager.getHotQuestionIdList();
        return assembleHotQuestionVo(hotQuestionIdList);
    }


    private List<HotQuestionVo> assembleHotQuestionVo(List<Long> hotQuestionId) {
        if (CollectionUtils.isEmpty(hotQuestionId)) {
            return new ArrayList<>();
        }
        // 从 ES 中获取数据
        List<QuestionES> questionESList = questionRepository.findByQuestionIdIn(hotQuestionId);
        if (questionESList.size() != hotQuestionId.size()) {
            refreshQuestion();
            questionESList = questionRepository.findByQuestionIdIn(hotQuestionId);
        }
        return questionESList.stream().map(QuestionES -> {
            HotQuestionVo hotQuestionVo = new HotQuestionVo();
            BeanUtils.copyProperties(QuestionES, hotQuestionVo);
            return hotQuestionVo;
        }).toList();
    }


    private void refreshQuestion() {
        List<Question> questionList = questionMapper.selectList(new LambdaQueryWrapper<>());
        if (CollectionUtils.isEmpty(questionList)) {
            return;
        }
        // 存储数据到 elasticsearch
        List<QuestionES> questionESList = BeanUtil.copyToList(questionList, QuestionES.class);
        questionRepository.saveAll(questionESList);
    }
}
